package com.mapforge.gui.tools;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.JToggleButton;

import com.mapforge.Constants;
import com.mapforge.listener.TileWriterChangeEvent;
import com.mapforge.listener.TileWriterChangeListener;
import com.mapforge.map.tools.MapToolInterface;
import com.mapforge.map.tools.selection.AbstractSelectionTool;
import com.mapforge.map.tools.selection.RectangleSelectionTool;
import com.mapforge.selection.SetOperationType;

/**
 * This class contains groups of buttons which represent various types of tile
 * selection, and a set of buttons which represent how any newly selected points
 * and previously selected points combine to form a new selection.
 * 
 * @author Will Morrison
 * 
 */
public class SelectionToolsPanel extends JPanel implements
		TileWriterChangeListener {

	private SetOperationType currentSelectionType;
	/**
	 * Default serial version ID, here to remove warnings.
	 */
	private static final long serialVersionUID = 1L;

	// selection tools
	//private EllipseSelectionTool ellipseSelectionTool;
	//private FloodFillSelectionTool floodFillSelectionTool;
	private RectangleSelectionTool rectangleSelectionTool;

	private JToggleButton btnAddSelection;
	private JToggleButton btnSubSelection;
	private JToggleButton btnIntersectSelection;
	private JToggleButton btnNewSelection;

	private List<MapToolButton> selectionToolButtons;
	private List<JToggleButton> selectionTypeButtons;
	private SelectionTypeListener selectionTypeListener;
	private SelectionToolListener selectionToolListener;

	private JPanel selectionToolPanel;
	private JPanel selectionToolTypePanel;

	private MapToolButton currentSelectionToolBtn;

	/**
	 * If this button isn't null, then its icon will be updated as the current
	 * selection type changes.
	 */
	private AbstractButton selectionIndicatorBtn;

	public SelectionToolsPanel(AbstractButton selectionIndicatorBtn) {
		super(new FlowLayout(FlowLayout.LEFT, 0, 0));

		selectionTypeListener = new SelectionTypeListener();
		selectionToolListener = new SelectionToolListener();

		selectionToolButtons = new ArrayList<MapToolButton>();
		selectionTypeButtons = new ArrayList<JToggleButton>();

		selectionToolPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 1, 1));
		selectionToolTypePanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 1,
				1));

		// initialize selection tools
		//ellipseSelectionTool = new EllipseSelectionTool();
		//floodFillSelectionTool = new FloodFillSelectionTool();
		rectangleSelectionTool = new RectangleSelectionTool();

		// init selection buttons, the creation method adds the listener,
		// and adds the buttons to the appropriate list and panel
		ButtonGroup selectionToolButtonGroup = new ButtonGroup();
		MapToolButton rectangleSelectionButton = createToolButton(
				Constants.RECTANGLE_SELECTION_ICON, rectangleSelectionTool,
				selectionToolButtonGroup);
		/*
		 * MapToolButton ellipseSelectionButton = createToolButton(
		 * Constants.ELLIPSE_SELECTION_ICON, ellipseSelectionTool,
		 * selectionToolButtonGroup); MapToolButton floodFillSelectionButton =
		 * createToolButton( Constants.FLOODFILL_SELECTION_ICON,
		 * floodFillSelectionTool, selectionToolButtonGroup);
		 */
		rectangleSelectionButton.setSelected(true);
		currentSelectionToolBtn = rectangleSelectionButton;
		add(selectionToolPanel);

		// create toggle buttons which control the type of selection
		// the selection tools use.
		ButtonGroup selectionTypeBtnGroup = new ButtonGroup();
		btnAddSelection = createToggleButton(Constants.ADD_SELECTION,
				"Add to selection", selectionTypeBtnGroup);
		btnSubSelection = createToggleButton(Constants.SUBTRACT_SELECTION,
				"Subtract from selection", selectionTypeBtnGroup);
		btnIntersectSelection = createToggleButton(
				Constants.INTERSECT_SELECTION, "Intersect with selection",
				selectionTypeBtnGroup);
		btnNewSelection = createToggleButton(Constants.NEW_SELECTION,
				"Create new selection", selectionTypeBtnGroup);
		add(selectionToolTypePanel);
		if (selectionIndicatorBtn != null)
			this.setSelectionIndicatorButton(selectionIndicatorBtn);
	}

	private MapToolButton createToolButton(ImageIcon icon,
			AbstractSelectionTool tool, ButtonGroup group) {
		MapToolButton btn = new MapToolButton(icon, tool, null);
		group.add(btn);
		selectionToolPanel.add(btn);
		selectionToolButtons.add(btn);
		btn.addActionListener(selectionToolListener);
		return btn;
	}

	private JToggleButton createToggleButton(ImageIcon icon, String tip,
			ButtonGroup group) {
		JToggleButton btn = new JToggleButton();
		btn.setIcon(icon);
		btn.setToolTipText(tip);
		btn.addActionListener(selectionTypeListener);
		group.add(btn);
		selectionTypeButtons.add(btn);
		selectionToolTypePanel.add(btn);
		return btn;
	}

	public AbstractSelectionTool getSelectedSelectionTool() {
		return (AbstractSelectionTool) currentSelectionToolBtn.getMapTool();
	}

	/**
	 * Sets the button who's icon will be set to match an icon representing the
	 * current selection tool.
	 * 
	 * @param button
	 */
	public void setSelectionIndicatorButton(AbstractButton button) {
		selectionIndicatorBtn = button;
	}

	/**
	 * Returns the button who's icon is set to match an icon representing the
	 * current selection tool.
	 * 
	 * @return
	 */
	public AbstractButton getSelectionIndicatorButton() {
		return selectionIndicatorBtn;
	}

	public void addSelectionButtonListener(ActionListener listener) {
		for (int i = 0; i < selectionToolButtons.size(); i++)
			selectionToolButtons.get(i).addActionListener(listener);
	}

	public void removeSelectionButtonListener(ActionListener listener) {
		for (int i = 0; i < selectionToolButtons.size(); i++)
			selectionToolButtons.get(i).removeActionListener(listener);
	}

	/**
	 * Called when the selection type is changed. All selection tools are
	 * iterated through and their selection types are set to the new selection
	 * type.
	 * 
	 * @param type
	 */
	private void setSetOperationType(SetOperationType type) {
		if (currentSelectionType != type) {
			for (int i = 0; i < selectionToolButtons.size(); i++) {
				MapToolInterface tool = selectionToolButtons.get(i)
						.getMapTool();
				if (tool instanceof AbstractSelectionTool) {
					AbstractSelectionTool selectionTool = (AbstractSelectionTool) tool;
					selectionTool.setSelectionType(type);
				}
			}
		}
	}

	/**
	 * A private listener which will change the selected indicator button's icon
	 * to match whatever type of tile selection tool is currently selected.
	 * 
	 * @author Will Morrison
	 * 
	 */
	private class SelectionToolListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			Object obj = e.getSource();
			if (obj != null && selectionIndicatorBtn != null
					&& obj instanceof MapToolButton) {
				MapToolButton btn = (MapToolButton) obj;
				selectionIndicatorBtn.setIcon(btn.getIcon());
				MapToolInterface tool = btn.getMapTool();
				if (tool instanceof AbstractSelectionTool) {
					currentSelectionToolBtn = btn;
				} else
					throw new IllegalArgumentException(
							"Non-selection tool found in selection panel."
									+ "\nSelection panel shouldn't contain tools which aren't selection tools.");
			}
		}
	}

	/**
	 * This private class listens to button presses on toggle buttons which
	 * control the type of selection various selection tools make to combine new
	 * selected tiles with any currently selected tiles.
	 * 
	 * @author Will Morrison
	 * 
	 */
	private class SelectionTypeListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			Object src = e.getSource();
			if (src == btnNewSelection) {
				setSetOperationType(SetOperationType.NEW);
			} else if (src == btnAddSelection) {
				setSetOperationType(SetOperationType.ADDITION);
			} else if (src == btnSubSelection) {
				setSetOperationType(SetOperationType.SUBTRACTION);
			} else if (src == btnIntersectSelection) {
				setSetOperationType(SetOperationType.INTERSECTION);
			} else
				throw new IllegalArgumentException(
						"This listener in the Selection panel should only be registered with the 4 types of selection.");
		}

	}

	@Override
	public void tileWriterChangePerformed(TileWriterChangeEvent event) {
		// Listen to tile writer changes so can update clip
	}
}